/**
 * 
 */
package com.csi.jf.crucible.plugins.jgit;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.eclipse.jgit.api.AddCommand;
import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.CommitCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.PushCommand;
import org.eclipse.jgit.api.errors.CheckoutConflictException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.diff.RenameDetector;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;

import com.csi.jf.crucible.api.util.StringUtils;

/**
 * @author daniel
 *
 */
public class JGit {
    

	private String git_home = "d:/git/gittest";
    
    private Git git = null;
    
	
    /**
     * 初始化Git home
     * @return
     * @throws Throwable
     */
    public File initGitHome()throws Throwable {
        File root = new File(git_home);
        if(!root.exists()){
            root.mkdir();
            System.out.println("Git home not exist,create!");
        }
        File gitF = new File(git_home+File.separator+"/.git");
        if(!gitF.exists()) {//如果已经初始化过,那肯定有.git文件夹
            //初始化git库,相当于命令行的 git init
            Git.init().setDirectory(root).call();
            System.out.println("Git home not initialized,init!");
        }
        git = Git.open(root); //打开git库
        return root;
    }
    
    /**
     * 初始化Git设置 
     * @throws URISyntaxException
     * @throws IOException
     */
    public void initGitConfig() throws URISyntaxException, IOException{
        Repository repo =null;
        // git = init.call();// 创建仓库
        repo = git.getRepository();
        System.out.println("Open the git repository");
        
        // 执行 git remote add 命令
        // 实例化一个RemoteConfig 对象，用户配置远端仓库
        StoredConfig config = repo.getConfig();
        RemoteConfig remoteConfig = new RemoteConfig(config, "origin");
        // 设置你的远端地址
        URIish uri = new URIish("https://zhangmh%40chinasofti.com@git.jointforce.com/scm/join/jftestrepo.git");
        System.out.println("Set remote-url: https://zhangmh%40chinasofti.com@git.jointforce.com/scm/join/jftestrepo.git");
        // 设置哪个分支
        RefSpec refSpec =new RefSpec("+refs/head/*:refs/remotes/origin/*");
        System.out.println("Set branch: +refs/head/*:refs/remotes/origin/*");
        // 更新配置
        remoteConfig.addFetchRefSpec(refSpec);
        remoteConfig.addPushRefSpec(refSpec);
        remoteConfig.addURI(uri);
        remoteConfig.addPushURI(uri);
        // 更新配置
        remoteConfig.update(config);
        // 保存到本地文件中
        config.save();
        System.out.println("Update and save git config");
    }
    
    /**
     * 添加并提交一个文件
     * @throws Throwable
     */
    public void addFile(String path,File srcFile)throws Throwable{
        if(srcFile==null ||!srcFile.exists()){
            return;
        }
        File destFile = new File(git_home+File.separator+srcFile.getName());
        FileUtils.copyFile(srcFile, destFile);
        System.out.println("Add a file:"+srcFile.getName());
        //好吧,随便写一个文件进去先
//        File newFile = new File(git_home+File.separator+"/"+System.currentTimeMillis()+".java");
//        FileWriter fw = new FileWriter(newFile);
//        fw.write(System.currentTimeMillis() + " ABC");
//        fw.flush();
//        fw.close();
        
        //添加文件咯,相当于 git add .
        git.add().addFilepattern(".+").call();
    }
    
    public void commit(List<String> files,String remark) throws Exception{
        if (StringUtils.isNotBlank(git_home) && files != null && files.size() > 0) {
            
        }
        //判断工作区与暂存区的文件内容是否有变更
        List<DiffEntry> diffEntries = git.diff().setPathFilter(PathFilterGroup.createFromStrings(files))
            .setShowNameAndStatusOnly(true).call();
        if (diffEntries == null || diffEntries.size() == 0) {
            throw new Exception("提交的文件内容都没有被修改，不能提交");
        }
        //被修改过的文件
        List<String> updateFiles = new ArrayList<String>();
        ChangeType changeType;
        for (DiffEntry entry : diffEntries) {
            changeType = entry.getChangeType();
            switch (changeType) {
                case ADD:
                case COPY:
                case RENAME:
                case MODIFY:
                    updateFiles.add(entry.getNewPath());
                    break;
                case DELETE:
                    updateFiles.add(entry.getOldPath());
                    break;
            }
        }
        
        //将文件提交到git仓库中，并返回本次提交的版本号
        //1、将工作区的内容更新到暂存区
        AddCommand addCmd = git.add();
        for (String file : updateFiles) {
            addCmd.addFilepattern(file);
        }
        addCmd.call();
        //2、commit
        CommitCommand commitCmd = git.commit();
        for (String file : updateFiles) {
            commitCmd.setOnly(file);
        }
        RevCommit revCommit = commitCmd.setCommitter("zhangmh", "zhangmh@chinasofti.com").setMessage(remark).call();
        String commitResult = revCommit.getName();
        System.out.println("Commit: "+commitResult);
    }
    
    public void pull()throws Exception{
        git.pull().call();
        System.out.println("Pull from remote!");
    }
    
    public void push() throws InvalidRemoteException, TransportException, GitAPIException{
    	 Repository repo =null;
         repo = git.getRepository();
         //JGit jGit=new JGit(repo);
         PushCommand pushCommand=git.push();
         Ref ref=null;
		try {
			ref = repo.getRef(git_home);
			pushCommand.add(ref);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		pushCommand.call();
         //git.push().call();
         System.out.println("=================>>>"+git.push().call());
         System.out.println("Push commit to remote!");
    }
    
    public void rebase() throws NoHeadException, RefNotFoundException, WrongRepositoryStateException, GitAPIException{
        git.rebase().call();
    }
    
    public void reset() throws CheckoutConflictException, GitAPIException{
        git.reset().call();
    }
    
    /**
     * 回滚到指定版本的上一个版本
     * @param gitRoot git仓库目录
     * @param diffEntries 需要回滚的文件
     * @param revision 版本号
     * @param remark 备注
     * @return
     * @throws Exception
     */
    public static boolean rollBackPreRevision(String gitRoot, List<DiffEntry> diffEntries,
                                              String revision, String remark) throws Exception {

        if (diffEntries == null || diffEntries.size() == 0) {
            throw new Exception("没有需要回滚的文件");
        }

        Git git = Git.open(new File(gitRoot));

        List<String> files = new ArrayList<String>();

        //注意：下面的reset命令会将暂存区的内容恢复到指定（revesion）的状态，相当于取消add命令的操作
        /*Repository repository = git.getRepository();

        RevWalk walk = new RevWalk(repository);
        ObjectId objId = repository.resolve(revision);
        RevCommit revCommit = walk.parseCommit(objId);
        String preVision = revCommit.getParent(0).getName();
        ResetCommand resetCmd = git.reset();
        for (String file : files) {
            resetCmd.addPath(file);
        }
        resetCmd.setRef(preVision).call();
        repository.close();*/

        //取出需要回滚的文件，新增的文件不回滚
        for (DiffEntry diffEntry : diffEntries) {
            if (diffEntry.getChangeType() == ChangeType.DELETE) {
                continue;
            } else {
                files.add(diffEntry.getNewPath());
            }
        }

        if (files.size() == 0) {
            throw new Exception("没有需要回滚的文件");
        }

        //checkout操作会丢失工作区的数据，暂存区和工作区的数据会恢复到指定（revision）的版本内容
        CheckoutCommand checkoutCmd = git.checkout();
        for (String file : files) {
            checkoutCmd.addPath(file);
        }
        //加了“^”表示指定版本的前一个版本，如果没有上一版本，在命令行中会报错，例如：error: pathspec '4.vm' did not match any file(s) known to git.
        checkoutCmd.setStartPoint(revision + "^");
        checkoutCmd.call();

        //重新提交一次
        CommitCommand commitCmd = git.commit();
        for (String file : files) {
            commitCmd.setOnly(file);
        }
        commitCmd.setCommitter("yonge", "654166020@qq.com").setMessage(remark).call();

        return true;
    }
    
    /**
     * 获取上一版本的变更记录，如果是新增的文件，不会显示，因为做回滚时不需要回滚新增的文件
     * @param gitRoot git仓库目录
     * @param revision 版本号
     * @return
     * @throws Exception
     */
    public static List<DiffEntry> rollBackFile(String gitRoot, String revision) throws Exception {

        Git git = Git.open(new File(gitRoot));
        Repository repository = git.getRepository();

        ObjectId objId = repository.resolve(revision);
        Iterable<RevCommit> allCommitsLater = git.log().add(objId).call();
        Iterator<RevCommit> iter = allCommitsLater.iterator();
        RevCommit commit = iter.next();
        TreeWalk tw = new TreeWalk(repository);
        tw.addTree(commit.getTree());
        commit = iter.next();
        if (commit != null) {
            tw.addTree(commit.getTree());
        } else {
            throw new Exception("当前库只有一个版本，不能获取变更记录");
        }

        tw.setRecursive(true);
        RenameDetector rd = new RenameDetector(repository);
        rd.addAll(DiffEntry.scan(tw));
        List<DiffEntry> diffEntries = rd.compute();
        if (diffEntries == null || diffEntries.size() == 0) {
            return diffEntries;
        }
        Iterator<DiffEntry> iterator = new ArrayList<DiffEntry>(diffEntries).iterator();
        DiffEntry diffEntry = null;
        while (iterator.hasNext()) {
            diffEntry = iterator.next();
            System.out.println("newPath:" + diffEntry.getNewPath() + "    oldPath:"
                               + diffEntry.getOldPath() + "   changeType:"
                               + diffEntry.getChangeType());
            if (diffEntry.getChangeType() == ChangeType.DELETE) {
                iterator.remove();
            }
        }
        return diffEntries;
    }
    
    public void showLog() throws NoHeadException, GitAPIException{
        //接下来,我们看看log信息
        for(RevCommit revCommit : git.log().call()){
            System.out.println(revCommit);
            System.out.println(revCommit.getFullMessage());
            System.out.println(revCommit.getCommitterIdent().getName() + " " + revCommit.getCommitterIdent().getEmailAddress());
        }
    }
    
    /** 
     * 查询本次提交的日志 
     * @param gitRoot git仓库 
     * @param revision  版本号 
     * @return  
     * @throws Exception 
     */
    public static List<DiffEntry> getLog(String gitRoot, String revision) throws Exception {  
        Git git = Git.open(new File(gitRoot));  
        Repository repository = git.getRepository();  
  
        ObjectId objId = repository.resolve(revision);  
        Iterable<RevCommit> allCommitsLater = git.log().add(objId).call();  
        Iterator<RevCommit> iter = allCommitsLater.iterator();  
        RevCommit commit = iter.next();  
        TreeWalk tw = new TreeWalk(repository);  
        tw.addTree(commit.getTree());  
  
        commit = iter.next();  
        if (commit != null)  
            tw.addTree(commit.getTree());  
        else  
            return null;  
  
        tw.setRecursive(true);  
        RenameDetector rd = new RenameDetector(repository);  
        rd.addAll(DiffEntry.scan(tw));  
  
        return rd.compute();  
    }  
    
 
    
    /**
     * 
     * @param args
     * @throws Throwable
     */
	public static void main(String[] args) throws Throwable {
//	    File file = new File("sonar-project.properties");
//	    System.out.println(file.exists());
	    JGit jgit = new JGit();
	    //0-创建repository
	    //1-初始化git home
	    jgit.initGitHome();
	    //2-初始化git settings
	    //jgit.initGitConfig();
	    //3-add文件到git home
	    File file = new File("build.properties");
	    jgit.addFile(null, file);
	    //4-commit文件
	    List<String> files = new ArrayList<String>();
        files.add(file.getName());
	    jgit.commit(files,"add new file:"+file.getName());
	    //5-pull
	    //jgit.pull();
	    //6-push
	    jgit.push();
	    //7-show logs
	    jgit.showLog();
	}
	
	public void testAddSubmodule() throws Exception {
//		String uri = library.getRepository().getDirectory().getCanonicalPath();
//		SubmoduleAddCommand addCommand = parent.submoduleAdd();
//		addCommand.setURI(uri);
//		addCommand.setPath("modules/library");
//		Repository repository = addCommand.call();
//		repository.close();
//
//		F‌ile workDir = parent.getRepository().getWorkTree();
//		F‌ile readme = new F‌ile(workDir, "modules/library/readme.txt");
//		F‌ile gitmodules = new F‌ile(workDir, ".gitmodules");
//		assertTrue(readme.isF‌ile());
//		assertTrue(gitmodules.isF‌ile());
	}

}
